/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package xenon3d.vector;

/**
 * This class represents a vector in 3 dimensional floating point vector space.
 * @author Volker Everts
 * @version 0.1 - 19.08.08: Created
 */
public class Vector3f extends Point3f {

    // <editor-fold defaultstate="collapsed" desc=" Static Attributes ">

    /** The serial version UID. */
    private static final long serialVersionUID = 20110917L;

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" Initialization ">

    /**
     * Creates a new Vector3f with the coordinates (0, 0, 0).
     */
    public Vector3f() {
        super();
    }

    /**
     * Creates a new Vector3f using the specified coordinates.
     * @param x the x coordinate
     * @param y the y coordinate
     * @param z the z coordinate
     */
    public Vector3f(float x, float y, float z) {
        super(x, y, z);
    }

    /**
     * Creates a new Vector3f with the same coordinates as the specified other
     * tuple, point or vector.
     * @param t the other tuple
     */
    public Vector3f(Tuple3f t) {
        super(t);
    }

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" Public Methods ">

    /**
     * Inverts this vector in place. Alias method for negate().
     */
    public final void invert() {
        x = -x;
        y = -y;
        z = -z;
    }

    /**
     * Sets the value of this vector to the inversion of the specified vector.
     * Alias method for negate(Vector3f).
     * @param v the other vector
     */
    public final void invert(Vector3f v) {
        x = -v.x;
        y = -v.y;
        z = -v.z;
    }

    /**
     * Normalizes this vector in place.
     */
    public final void normalize() {
        float norm = lengthSq();
        if (FMath.isEqual(norm, 1.0f, FMath.EPS)) return;
        if (norm > FMath.EPS) {
            norm = FMath.sqrt(norm);
            x /= norm;
            y /= norm;
            z /= norm;
        }
        else set(0.0f, 0.0f, 0.0f);
    }

    /**
     * Sets the value of this vector to the normalization of the specified vector.
     * @param v the other vector
     */
    public final void normalize(Vector3f v) {
        set(v);
        normalize();
    }

    /**
     * Returns the length of this vector.
     * @return the length of this vector
     */
    public final float length() {
        return FMath.sqrt(x * x + y * y + z * z);
    }

    /**
     * Returns the squared length of this vector.
     * @return the squared length
     */
    public final float lengthSq() {
        return x * x + y * y + z * z;
    }

    /**
     * Returns the inner product of this vector and another vector or point,
     * given by its x,y,z coordinates.
     * @param x the other point's x coordinate
     * @param y the other point's y coordinate
     * @param z the other point's z coordinate
     * @return the dot product
     */
    public final float dot(float x, float y, float z) {
        return this.x * x + this.y * y + this.z * z;
    }

    /**
     * Returns the inner product of this vector and another vector or point.
     * @param p the other vector (or point)
     * @return the dot product
     */
    public final float dot(Point3f p) {
        return x * p.x + y * p.y + z * p.z;
    }

    /**
     * Returns the angle between this vector and another vector.
     * @param v the other vector
     * @return the angle between this vector and the other vector
     */
    public final float angle(Vector3f v) {
        float cos = dot(v) / FMath.sqrt(lengthSq() * v.lengthSq());
        if (cos < -1.0f) cos = -1.0f;
        else if (cos > 1.0f) cos = 1.0f;
        return FMath.acos(cos);
    }

    /**
     * Sets this vector to be the vector cross product of itself and another
     * vector. In mathematical terms: this = this x v
     * @param v the other vector
     */
    public final void cross(Vector3f v) {
        cross(this, v);
    }

    /**
     * Sets this vector to be the vector cross product of vectors v1 and v2.
     * In mathematical terms: this = v1 x v2
     * @param v1 the first vector
     * @param v2 the second vector
     */
    public final void cross(Vector3f v1, Vector3f v2) {
        float a = v1.y * v2.z - v1.z * v2.y;
        float b = v2.x * v1.z - v2.z * v1.x;
        float c = v1.x * v2.y - v1.y * v2.x;
        x = a;
        y = b;
        z = c;
    }

    /**
     * Rotates this vector by a specified number of degrees around the Y axis and
     * the specified center.
     * @param angle the number of degrees to rotate around the Y axis
     * @param center the center of rotation, or null, to rotate around (0, 0, 0)
     */
    public final void rotateY(float angle, Point3f center) {
        float cos = FMath.cos(angle);
        float sin = FMath.sin(angle);
        if (center != null) {
            x -= center.x;
            z -= center.z;
        }
        float u = x * cos - z * sin;
        float v = x * sin + z * cos;
        if (center != null) {
            x = u + center.x;
            z = v + center.z;
        }
        else {
            x = u;
            z = v;
        }
    }

    /**
     * Rotates the vector by a specified number of degrees around the Z axis and
     * the specified center.
     * @param angle the number of degrees to rotate around the Z axis
     * @param center the center of rotation, or null, to rotate around (0, 0, 0)
     */
    public final void rotateZ(float angle, Point3f center) {
        float cos = FMath.cos(angle);
        float sin = FMath.sin(angle);
        if (center != null) {
            x -= center.x;
            y -= center.y;
        }
        float u = x * cos - y * sin;
        float v = x * sin + y * cos;
        if (center != null) {
            x = u + center.x;
            y = v + center.y;
        }
        else {
            x = u;
            y = v;
        }
    }

    /**
     * Rotates the vector by a specified number of degrees around the X axis and
     * the specified center.
     * @param angle the number of degrees to rotate around the X axis
     * @param center the center of rotation, or null, to rotate around (0, 0, 0)
     */
    public final void rotateX(float angle, Point3f center) {
        float cos = FMath.cos(angle);
        float sin = FMath.sin(angle);
        if (center != null) {
            z -= center.z;
            y -= center.y;
        }
        float u = y * cos - z * sin;
        float v = y * sin + z * cos;
        if (center != null) {
            z = u + center.z;
            y = v + center.y;
        }
    }

    // </editor-fold>

} // end class Vector3f